import time
from typing import Iterator, List, NoReturn, Optional, Tuple

from sty import bg as bgr

try:
    from ._types import bg_importance, txt_importance
    from .entity import Entity
    from .event import Event
    from .utils import to_fullwidth
except ImportError:
    from _types import bg_importance, txt_importance  # type: ignore
    from entity import Entity  # type: ignore
    from event import Event  # type: ignore
    from utils import to_fullwidth  # type: ignore

__all__ = ['Block']


class Block:
    def __init__(self, board=None, position: Tuple[int, int] = None, entities: Optional[List[Entity]] = None):
        """A block in a board.

        Args:
            - `board` (`Board`, optional): The board.
            - `position` (`Tuple[int, int]`, optional): The position of
            this block on the board.
            - `entities` (`List[Entity]`, optional): The entities currently
            in this block. Defaults to `[]`.
        """
        self.board = board
        self.position = position
        self.blink: bool = False
        self.entities: List[Entity] = [] if entities is None else entities

    def __iter__(self) -> Iterator:
        return iter(self.entities)

    def __str__(self) -> str:
        t = time.time()
        if self.blink and t - round(t) < 0.5:
            return bgr.li_yellow + to_fullwidth(' ') + bgr.rs
        ret = ''
        txt = ' '
        txti = -1
        bg = None
        bgi = -1
        for e in self.entities:
            if e.char_rep() is not None:
                if txti < 0 or txt_importance.index(e.__class__) < txti:
                    txt = e.char_rep()
                    txti = txt_importance.index(e.__class__)
            if e.backgrnd_rep() is not None:
                if bgi < 0 or bg_importance.index(e.__class__) < bgi:
                    bg = e.backgrnd_rep()
                    bgi = bg_importance.index(e.__class__)
        if bgi >= 0:
            ret += bg[0]
        ret += to_fullwidth(txt)
        if bgi >= 0:
            ret += bg[1]
        return ret

    def __bool__(self) -> bool:
        return bool(self.entities)

    def event_trigger(self, ev: Event) -> NoReturn:
        """Call this when an event is triggered.

        Args:
            - `ev` (`Event`): The triggered event.
        """
        for e in self.entities:
            callback = e.onevent(ev)
            if callable(callback):
                callback(self.board, self.position, ev)

    def empty(self) -> bool:
        """Returns whether this block has no entities.

        Returns:
            - `bool`: Whether this block has no entities.
        """
        return not len(self.entities)

    def place_river(self) -> bool:
        """Returns whether a river can be placed at this spot.

        Returns:
            - `bool`: Whether a river can be placed here.
        """
        return all([x.place_river() for x in self.entities])

    def place_mountain(self) -> bool:
        """Returns whether a mountain can be placed at this spot.

        Returns:
            - `bool`: Whether a mountain can be placed here.
        """
        return all([x.place_mountain() for x in self.entities])

    def place_tree(self) -> bool:
        """Returns whether a tree can be placed at this spot.

        Returns:
            - `bool`: Whether a tree can be placed here.
        """
        return all([x.place_tree() for x in self.entities])

    def place_building(self, building: Entity) -> bool:
        """Returns whether a building can be placed at this spot.

        Args:
            - `building` (`Building`): The building to place.

        Returns:
            - `bool`: Whether the building can be placed here.
        """
        return all([x.place_building(building) for x in self.entities])

    def crossable(self, unit: Entity) -> bool:
        """Returns whether a unit can cross this block.

        Args:
            - `unit` (`Unit`): The unit that is trying to cross the block.

        Returns:
            - `bool`: Whether a unit can cross this block.
        """
        return all([x.crossable(unit) for x in self.entities])

    def append(self, entity: Entity) -> NoReturn:
        """Appends the entity to the block.

        Args:
            - `entity` (`Entity`): The entity to be appended.
        """
        self.entities.append(entity)
        if self.board:
            self.board.event_trigger(Event('append_entity'))

    def remove(self, entity: Entity) -> NoReturn:
        """Removes the entity from the block.

        Args:
            - `entity` (`Entity`): The entity to be removed.
        """
        self.entities.remove(entity)
